#undef SEEK_SET
#undef SEEK_CUR
#undef SEEK_END
#include "mpicalculator.h"
#include "parafluidmpi.h"
#include "mpi.h"
#include "stdio.h"
#include <cstdlib>

#define NUM_ATOM		2

#define TAG_ALL_ATOMS	100
#define TAG_ATOM  		101
#define TAG_FIRST_ATOM	102
#define TAG_LAST_ATOM	103

using namespace ParaFluid;

void sendAllAtoms(int node, char atoms[NUM_ATOM], int numByteToSend)
{
	MPI_Send(atoms, numByteToSend, MPI_CHAR, node, TAG_ALL_ATOMS, MPI_COMM_WORLD);
	printf("AllAtoms send to %i...\n", node);
}

void receiveAllAtoms(int node, char atoms[NUM_ATOM], int numByteToReceive)
{
	MPI_Status status;
	
	MPI_Recv(&(atoms[0]), numByteToReceive, MPI_CHAR, node, TAG_ALL_ATOMS, MPI_COMM_WORLD, &status);
	printf("AllAtoms received from %i!...\n", node);
}

void sendAtom(int node, char * atom, int numByteToSend)
{
	MPI_Send(atom, numByteToSend, MPI_CHAR, node, TAG_ATOM, MPI_COMM_WORLD);
}

void receiveAtom(int node, char & atom, int numByteToReceive)
{
	MPI_Status status;

	MPI_Recv(&atom, numByteToReceive, MPI_CHAR, node, TAG_ATOM, MPI_COMM_WORLD, &status);
}

void sendSequenceToCompute(int node, int firstAtom, int lastAtom)
{
	MPI_Send(&firstAtom, 4, MPI_INT, node, TAG_FIRST_ATOM, MPI_COMM_WORLD);
	MPI_Send(&lastAtom,  4, MPI_INT, node, TAG_LAST_ATOM,  MPI_COMM_WORLD);
	printf("Sequence to compute [%i -> %i] send to %i...\n", node, firstAtom, lastAtom);
}

void receiveSequenceToCompute(int node, int & firstAtom, int & lastAtom)
{
	MPI_Status status;

	MPI_Recv(&firstAtom, 4, MPI_INT, node, TAG_FIRST_ATOM, MPI_COMM_WORLD, &status);
	MPI_Recv(&lastAtom,  4, MPI_INT, node, TAG_LAST_ATOM,  MPI_COMM_WORLD, &status);
	printf("Sequence to compute [%i -> %i] received from %i...\n", node, firstAtom, lastAtom);
}

void MpiCalculator::process()
{
	bool compute = true;
    int numNode = 0;
    int currentNode = 0;
    int firstAtom = 0;
    int lastAtom = 0;
    
   	//printf("MPI_Init(0, 0);\n");
    MPI_Init(0, 0);
    MPI_Comm_size(MPI_COMM_WORLD, &numNode);
    MPI_Comm_rank(MPI_COMM_WORLD, &currentNode);
    int numAtomsPerNode = NUM_ATOM / numNode;
    
    // Initialiser le buffer d'atomes
	Mpi mpi;
	int bufferSize;
	char * buffer;
	mpi.getAtom(buffer, bufferSize); // Comment?? Dieu seul le sait!
   	
	//printf("BUFFER SIZE %d\n", bufferSize);
	//printf("Buffer full!\n");
	
    while(compute) 
	//for(int i =0; i < 1; i++)		
	{
		//printf("i = %i\n", i);
        // Premier noeud
        if(currentNode == 0)
        {

            // Pour tous les noeuds
            for(int node = 1; node < numNode; ++node) 
            {
            	// Envoyer Tous les atomes aux autres noeuds
            	sendAllAtoms(node, buffer, bufferSize);
            	
            	// Envoyer le debut et la fin de la 
            	// sequence d'atome à calculer au noeud
				firstAtom = node * numAtomsPerNode;
                lastAtom = firstAtom + numAtomsPerNode;
                sendSequenceToCompute(node, firstAtom, lastAtom);
            }
            
            // Calculer la premiere tache de 0 @ Nombre d'atome par noeud
            firstAtom = 0;
            lastAtom = numAtomsPerNode;
        
			// Calculer la premiere partie
			// TODO ********

            // Recevoir les resultats des autres noeuds
            for(int node = 1; node < numNode; ++node) 
            { 
            	char * bufferRecv = (char *) malloc(80 * );
        		receiveAllAtoms(0, bufferRecv, bufferSize);

                receiveSomeAtoms(node, bufferRecv, );
				printf("receiveAllAtoms(node, bufferRecv);\n");
                mpi.showAtom(bufferRecv, bufferSize);
            }
     /*      
            // Passer a la prochaine Genration
            nbCells = calculerNbCellules(cases);
            numGen++;
            printf("Generation %i: %i cellulles.\n", numGen, nbCells);
            
            // Afficher
            afficher(cases);
            
            // Continuer
            if(nbCells <= 0) {
               
               continuer = false;
               printf("Termine. Toutes les cellules sont mortes.\n");
               
            } else if(nbCells >= 250) {
               
               continuer = false;
               printf("Termine. Trop de cellules. (Max 250)\n");
               
            //} else if(numGen >= 1000) {
            //   
            //   continuer = false;
            //   printf("Termine. Derniere generation. (Max 1000)\n");
            //   
            }mpi.
            for(int noeud = 1; noeud < nbNoeuds; ++noeud) {
               MPI_Send(&continuer, 1, MPI_CHAR, noeud, TAG_CONTINUER, MPI_COMM_WORLD);
            }
            if(!continuer){
		 endtime   = MPI_Wtime(); 
    		printf("\nle temps d'execution = %f seconds\n",endtime-starttime);
*/    
        // Autre noeuds
        } 
		else 
		{
			//printf("currentNode %d\n",currentNode);
            char * bufferRecv = (char *) malloc(bufferSize);
        	receiveAllAtoms(0, bufferRecv, bufferSize);
			mpi.showAtom(bufferRecv, bufferSize);
			//printf("a");
			//mpi.showAtom(bufferRecv, bufferSize);
									
/*
           // Recevoir tache
           recevoirLignes(0, cases, ligneDebut, ligneFin, ligneDebutCalcul, ligneFinCalcul);
           
           // Calculer tache
           prochaineGeneration(cases, ligneDebut, ligneFin, ligneDebutCalcul, ligneFinCalcul);
           
           // Envoyer resultat
           envoyerLignes(0, cases, ligneDebutCalcul, ligneFinCalcul, ligneDebutCalcul, ligneFinCalcul);
           
           // Continuer
           MPI_Status status;
           MPI_Recv(&continuer, 1, MPI_CHAR, 0, TAG_CONTINUER, MPI_COMM_WORLD, &status);
           
*/
		}
    }
   
    MPI_Finalize();
    
    
}


void MpiCalculator::init(int & numNode, int & currentNode)
{
	MPI_Init(0, 0);
	MPI_Comm_size(MPI_COMM_WORLD, &numNode);
	MPI_Comm_rank(MPI_COMM_WORLD, &currentNode);
}

void MpiCalculator::finalize()
{
	MPI_Finalize();
}



